Skip to content

Conversation

Andres-Salamanca
Copy link
Contributor

This PR adds support for loading and storing volatile bit-field members according to the AAPCS specification.

A volatile bit-field must always be accessed using an access width appropriate to the type of its container, except when any of the following are true:

  • The bit-field container overlaps with a zero-length bit-field.
  • The bit-field container overlaps with a non-bit-field member.

For example, if a bit-field is declared as int, the load/store must use a 32-bit access, even if the field itself is only 3 bits wide.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Aug 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 3, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: None (Andres-Salamanca)

Changes

This PR adds support for loading and storing volatile bit-field members according to the AAPCS specification.

> A volatile bit-field must always be accessed using an access width appropriate to the type of its container, except when any of the following are true:
>
> * The bit-field container overlaps with a zero-length bit-field.
> * The bit-field container overlaps with a non-bit-field member.

For example, if a bit-field is declared as int, the load/store must use a 32-bit access, even if the field itself is only 3 bits wide.


Full diff: https://github.com/llvm/llvm-project/pull/151875.diff

4 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+23-7)
  • (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+16-9)
  • (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp (+3-2)
  • (modified) clang/test/CIR/CodeGen/aapcs-volatile-bitfields.c (+225-13)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 73c9fb924f682..ff8e12190c972 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -410,21 +410,37 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
                                 Address dstAddr, mlir::Type storageType,
                                 mlir::Value src, const CIRGenBitFieldInfo &info,
-                                bool isLvalueVolatile) {
+                                bool isLvalueVolatile, bool useVolatile) {
+    unsigned offset = useVolatile ? info.volatileOffset : info.offset;
+
+    // If using AAPCS and the field is volatile, load with the size of the
+    // declared field
+    storageType =
+        useVolatile ? cir::IntType::get(storageType.getContext(),
+                                        info.volatileStorageSize, info.isSigned)
+                    : storageType;
     return create<cir::SetBitfieldOp>(
         loc, resultType, dstAddr.getPointer(), storageType, src, info.name,
-        info.size, info.offset, info.isSigned, isLvalueVolatile,
+        info.size, offset, info.isSigned, isLvalueVolatile,
         dstAddr.getAlignment().getAsAlign().value());
   }
 
   mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
                                 Address addr, mlir::Type storageType,
                                 const CIRGenBitFieldInfo &info,
-                                bool isLvalueVolatile) {
-    return create<cir::GetBitfieldOp>(
-        loc, resultType, addr.getPointer(), storageType, info.name, info.size,
-        info.offset, info.isSigned, isLvalueVolatile,
-        addr.getAlignment().getAsAlign().value());
+                                bool isLvalueVolatile, bool useVolatile) {
+    unsigned offset = useVolatile ? info.volatileOffset : info.offset;
+
+    // If using AAPCS and the field is volatile, load with the size of the
+    // declared field
+    storageType =
+        useVolatile ? cir::IntType::get(storageType.getContext(),
+                                        info.volatileStorageSize, info.isSigned)
+                    : storageType;
+    return create<cir::GetBitfieldOp>(loc, resultType, addr.getPointer(),
+                                      storageType, info.name, info.size, offset,
+                                      info.isSigned, isLvalueVolatile,
+                                      addr.getAlignment().getAsAlign().value());
   }
 };
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index cd37a2bd276bc..574a46715c2ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -322,22 +322,27 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
   assert(!cir::MissingFeatures::opTBAA());
 }
 
+/// Helper method to check if the underlying ABI is AAPCS
+static bool isAAPCS(const TargetInfo &targetInfo) {
+  return targetInfo.getABI().starts_with("aapcs");
+}
+
 mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
                                                            LValue dst) {
 
-  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
-
   const CIRGenBitFieldInfo &info = dst.getBitFieldInfo();
   mlir::Type resLTy = convertTypeForMem(dst.getType());
   Address ptr = dst.getBitFieldAddress();
 
-  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
+  bool useVoaltile = cgm.getCodeGenOpts().AAPCSBitfieldWidth &&
+                     dst.isVolatileQualified() &&
+                     info.volatileStorageSize != 0 && isAAPCS(cgm.getTarget());
 
   mlir::Value dstAddr = dst.getAddress().getPointer();
 
   return builder.createSetBitfield(dstAddr.getLoc(), resLTy, ptr,
                                    ptr.getElementType(), src.getValue(), info,
-                                   dst.isVolatileQualified());
+                                   dst.isVolatileQualified(), useVoaltile);
 }
 
 RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
@@ -347,10 +352,12 @@ RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
   mlir::Type resLTy = convertType(lv.getType());
   Address ptr = lv.getBitFieldAddress();
 
-  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
+  bool useVoaltile = lv.isVolatileQualified() && info.volatileOffset != 0 &&
+                     isAAPCS(cgm.getTarget());
 
-  mlir::Value field = builder.createGetBitfield(
-      getLoc(loc), resLTy, ptr, ptr.getElementType(), info, lv.isVolatile());
+  mlir::Value field =
+      builder.createGetBitfield(getLoc(loc), resLTy, ptr, ptr.getElementType(),
+                                info, lv.isVolatile(), useVoaltile);
   assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck() && "NYI");
   return RValue::get(field);
 }
@@ -375,10 +382,10 @@ LValue CIRGenFunction::emitLValueForBitField(LValue base,
   const CIRGenRecordLayout &layout =
       cgm.getTypes().getCIRGenRecordLayout(field->getParent());
   const CIRGenBitFieldInfo &info = layout.getBitFieldInfo(field);
-  assert(!cir::MissingFeatures::armComputeVolatileBitfields());
+
   assert(!cir::MissingFeatures::preservedAccessIndexRegion());
-  unsigned idx = layout.getCIRFieldNo(field);
 
+  unsigned idx = layout.getCIRFieldNo(field);
   Address addr = getAddrOfBitFieldStorage(base, field, info.storageType, idx);
 
   mlir::Location loc = getLoc(field->getLocation());
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index ecf31a7024987..1764967329969 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -847,8 +847,9 @@ void CIRRecordLowering::computeVolatileBitfields() {
 
       const CharUnits fEnd =
           fOffset +
-          astContext.toCharUnitsFromBits(astContext.toBits(
-              getSizeInBits(cirGenTypes.convertTypeForMem(f->getType())))) -
+          astContext.toCharUnitsFromBits(
+              getSizeInBits(cirGenTypes.convertTypeForMem(f->getType()))
+                  .getQuantity()) -
           CharUnits::One();
       // If no overlap, continue.
       if (end < fOffset || fEnd < storageOffset)
diff --git a/clang/test/CIR/CodeGen/aapcs-volatile-bitfields.c b/clang/test/CIR/CodeGen/aapcs-volatile-bitfields.c
index 3643cf257933e..00378f725d76a 100644
--- a/clang/test/CIR/CodeGen/aapcs-volatile-bitfields.c
+++ b/clang/test/CIR/CodeGen/aapcs-volatile-bitfields.c
@@ -1,8 +1,13 @@
 // RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -fclangir -emit-cir -fdump-record-layouts %s -o %t.cir 1> %t.cirlayout
 // RUN: FileCheck --input-file=%t.cirlayout %s --check-prefix=CIR-LAYOUT
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+
+// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
 
 // RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -emit-llvm -fdump-record-layouts %s -o %t.ll 1> %t.ogcglayout
 // RUN: FileCheck --input-file=%t.ogcglayout %s --check-prefix=OGCG-LAYOUT
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
 
 typedef struct  {
     unsigned int a : 9;
@@ -53,21 +58,228 @@ typedef struct{
 
 typedef struct{
     volatile unsigned int a : 3;
-    unsigned int z: 2;
-    volatile unsigned int b : 5;
+    unsigned int z;
+    volatile unsigned long b : 16;
 } st4;
 
 // CIR-LAYOUT: BitFields:[
-// CIR-LAYOUT-NEXT:   <CIRBitFieldInfo name:a offset:0 size:3 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:0 volatileStorageSize:32 volatileStorageOffset:0>
-// CIR-LAYOUT-NEXT:   <CIRBitFieldInfo name:z offset:3 size:2 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:3 volatileStorageSize:32 volatileStorageOffset:0>
-// CIR-LAYOUT-NEXT:   <CIRBitFieldInfo name:b offset:5 size:5 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:5 volatileStorageSize:32 volatileStorageOffset:0>
+// CIR-LAYOUT-NEXT:   <CIRBitFieldInfo name:a offset:0 size:3 isSigned:0 storageSize:8 storageOffset:0 volatileOffset:0 volatileStorageSize:32 volatileStorageOffset:0>
+// CIR-LAYOUT-NEXT:   <CIRBitFieldInfo name:b offset:0 size:16 isSigned:0 storageSize:16 storageOffset:8 volatileOffset:0 volatileStorageSize:64 volatileStorageOffset:1>
 
 // OGCG-LAYOUT: BitFields:[
-// OGCG-LAYOUT-NEXT:   <CGBitFieldInfo Offset:0 Size:3 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:32 VolatileStorageOffset:0>
-// OGCG-LAYOUT-NEXT:   <CGBitFieldInfo Offset:3 Size:2 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:3 VolatileStorageSize:32 VolatileStorageOffset:0>
-// OGCG-LAYOUT-NEXT:   <CGBitFieldInfo Offset:5 Size:5 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:5 VolatileStorageSize:32 VolatileStorageOffset:0>
-
-st1 s1;
-st2 s2;
-st3 s3;
-st4 s4;
+// OGCG-LAYOUT-NEXT:   <CGBitFieldInfo Offset:0 Size:3 IsSigned:0 StorageSize:8 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:32 VolatileStorageOffset:0>
+// OGCG-LAYOUT-NEXT:   <CGBitFieldInfo Offset:0 Size:16 IsSigned:0 StorageSize:16 StorageOffset:8 VolatileOffset:0 VolatileStorageSize:64 VolatileStorageOffset:1>
+
+
+void def () {
+  st1 s1;
+  st2 s2;
+  st3 s3;
+  st4 s4;
+}
+
+int check_load(st1 *s1) {
+  return s1->b;
+}
+
+// CIR:  cir.func dso_local @check_load
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_st1>>, !cir.ptr<!rec_st1>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][0] {name = "b"} : !cir.ptr<!rec_st1> -> !cir.ptr<!u16i>
+// CIR:    [[BITFI:%.*]] = cir.get_bitfield align(4) (#bfi_b, [[MEMBER]] {is_volatile} : !cir.ptr<!u16i>) -> !u32i
+// CIR:    [[CAST:%.*]] = cir.cast(integral, [[BITFI]] : !u32i), !s32i
+// CIR:    cir.store [[CAST]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i>
+// CIR:    [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:    cir.return [[RET]] : !s32i
+
+// LLVM:define dso_local i32 @check_load
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.st1, ptr [[LOAD]], i32 0, i32 0
+// LLVM:  [[LOADVOL:%.*]] = load volatile i32, ptr [[MEMBER]], align 4
+// LLVM:  [[LSHR:%.*]] = lshr i32 [[LOADVOL]], 9
+// LLVM:  [[CLEAR:%.*]] = and i32 [[LSHR]], 1
+// LLVM:  store i32 [[CLEAR]], ptr [[RETVAL:%.*]], align 4
+// LLVM:  [[RET:%.*]] = load i32, ptr [[RETVAL]], align 4
+// LLVM:  ret i32 [[RET]]
+
+// OGCG: define dso_local i32 @check_load
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[LOADVOL:%.*]] = load volatile i32, ptr [[LOAD]], align 4
+// OGCG:   [[LSHR:%.*]] = lshr i32 [[LOADVOL]], 9
+// OGCG:   [[CLEAR:%.*]] = and i32 [[LSHR]], 1
+// OGCG:   ret i32 [[CLEAR]]
+
+// this volatile bit-field container overlaps with a zero-length bit-field,
+// so it may be accessed without using the container's width.
+int check_load_exception(st3 *s3) {
+  return s3->b;
+}
+
+// CIR:  cir.func dso_local @check_load_exception
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_st3>>, !cir.ptr<!rec_st3>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][2] {name = "b"} : !cir.ptr<!rec_st3> -> !cir.ptr<!u8i>
+// CIR:    [[BITFI:%.*]] = cir.get_bitfield align(4) (#bfi_b1, [[MEMBER]] {is_volatile} : !cir.ptr<!u8i>) -> !u32i
+// CIR:    [[CAST:%.*]] = cir.cast(integral, [[BITFI]] : !u32i), !s32i
+// CIR:    cir.store [[CAST]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i>
+// CIR:    [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:    cir.return [[RET]] : !s32i
+
+// LLVM:define dso_local i32 @check_load_exception
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.st3, ptr [[LOAD]], i32 0, i32 2
+// LLVM:  [[LOADVOL:%.*]] = load volatile i8, ptr [[MEMBER]], align 4
+// LLVM:  [[CLEAR:%.*]] = and i8 [[LOADVOL]], 31
+// LLVM:  [[CAST:%.*]] = zext i8 [[CLEAR]] to i32
+// LLVM:  store i32 [[CAST]], ptr [[RETVAL:%.*]], align 4
+// LLVM:  [[RET:%.*]] = load i32, ptr [[RETVAL]], align 4
+// LLVM:  ret i32 [[RET]]
+
+// OGCG: define dso_local i32 @check_load_exception
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[MEMBER:%.*]] = getelementptr inbounds nuw %struct.st3, ptr [[LOAD]], i32 0, i32 2
+// OGCG:   [[LOADVOL:%.*]] = load volatile i8, ptr [[MEMBER]], align 4
+// OGCG:   [[CLEAR:%.*]] = and i8 [[LOADVOL]], 31
+// OGCG:   [[CAST:%.*]] = zext i8 [[CLEAR]] to i32
+// OGCG:   ret i32 [[CAST]]
+
+typedef struct {
+    volatile int a : 24;
+    char b;
+    volatile int c: 30;
+ } clip;
+
+int clip_load_exception2(clip *c) {
+  return c->a;
+}
+
+// CIR:  cir.func dso_local @clip_load_exception2
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_clip>>, !cir.ptr<!rec_clip>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][0] {name = "a"} : !cir.ptr<!rec_clip> -> !cir.ptr<!cir.array<!u8i x 3>>
+// CIR:    [[BITFI:%.*]] = cir.get_bitfield align(4) (#bfi_a1, [[MEMBER]] {is_volatile} : !cir.ptr<!cir.array<!u8i x 3>>) -> !s32i
+// CIR:    cir.store [[BITFI]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i>
+// CIR:    [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:    cir.return [[RET]] : !s32i
+
+// LLVM:define dso_local i32 @clip_load_exception2
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.clip, ptr [[LOAD]], i32 0, i32 0
+// LLVM:  [[LOADVOL:%.*]] = load volatile i24, ptr [[MEMBER]], align 4
+// LLVM:  [[CAST:%.*]] = sext i24 [[LOADVOL]] to i32
+// LLVM:  store i32 [[CAST]], ptr [[RETVAL:%.*]], align 4
+// LLVM:  [[RET:%.*]] = load i32, ptr [[RETVAL]], align 4
+// LLVM:  ret i32 [[RET]]
+
+// OGCG: define dso_local i32 @clip_load_exception2
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[LOADVOL:%.*]] = load volatile i24, ptr [[LOAD]], align 4
+// OGCG:   [[CAST:%.*]] = sext i24 [[LOADVOL]] to i32
+// OGCG:   ret i32 [[CAST]]
+
+void check_store(st2 *s2) {
+  s2->a = 1;
+}
+
+// CIR:  cir.func dso_local @check_store
+// CIR:    [[CONST:%.*]] = cir.const #cir.int<1> : !s32i
+// CIR:    [[CAST:%.*]] = cir.cast(integral, [[CONST]] : !s32i), !s16i
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_st2>>, !cir.ptr<!rec_st2>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][0] {name = "a"} : !cir.ptr<!rec_st2> -> !cir.ptr<!u32i>
+// CIR:    [[SETBF:%.*]] = cir.set_bitfield align(8) (#bfi_a, [[MEMBER]] : !cir.ptr<!u32i>, [[CAST]] : !s16i) {is_volatile} -> !s16i
+// CIR:    cir.return
+
+// LLVM:define dso_local void @check_store
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.st2, ptr [[LOAD]], i32 0, i32 0
+// LLVM:  [[LOADVOL:%.*]] = load volatile i16, ptr [[MEMBER]], align 8
+// LLVM:  [[CLEAR:%.*]] = and i16 [[LOADVOL]], -8
+// LLVM:  [[SET:%.*]] = or i16 [[CLEAR]], 1
+// LLVM:  store volatile i16 [[SET]], ptr [[MEMBER]], align 8
+// LLVM:  ret void
+
+// OGCG: define dso_local void @check_store
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[LOADVOL:%.*]] = load volatile i16, ptr [[LOAD]], align 8
+// OGCG:   [[CLEAR:%.*]] = and i16 [[LOADVOL]], -8
+// OGCG:   [[SET:%.*]] = or i16 [[CLEAR]], 1
+// OGCG:   store volatile i16 [[SET]], ptr [[LOAD]], align 8
+// OGCG:   ret void
+
+// this volatile bit-field container overlaps with a zero-length bit-field,
+// so it may be accessed without using the container's width.
+void check_store_exception(st3 *s3) {
+  s3->b = 2;
+}
+
+// CIR:  cir.func dso_local @check_store_exception
+// CIR:    [[CONST:%.*]] = cir.const #cir.int<2> : !s32i
+// CIR:    [[CAST:%.*]] = cir.cast(integral, [[CONST]] : !s32i), !u32i
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_st3>>, !cir.ptr<!rec_st3>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][2] {name = "b"} : !cir.ptr<!rec_st3> -> !cir.ptr<!u8i>
+// CIR:    [[SETBF:%.*]] = cir.set_bitfield align(4) (#bfi_b1, [[MEMBER]] : !cir.ptr<!u8i>, [[CAST]] : !u32i) {is_volatile} -> !u32i
+// CIR:    cir.return
+
+// LLVM:define dso_local void @check_store_exception
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.st3, ptr [[LOAD]], i32 0, i32 2
+// LLVM:  [[LOADVOL:%.*]] = load volatile i8, ptr [[MEMBER]], align 4
+// LLVM:  [[CLEAR:%.*]] = and i8 [[LOADVOL]], -32
+// LLVM:  [[SET:%.*]] = or i8 [[CLEAR]], 2
+// LLVM:  store volatile i8 [[SET]], ptr [[MEMBER]], align 4
+// LLVM:  ret void
+
+// OGCG: define dso_local void @check_store_exception
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[MEMBER:%.*]] = getelementptr inbounds nuw %struct.st3, ptr [[LOAD]], i32 0, i32 2
+// OGCG:   [[LOADVOL:%.*]] = load volatile i8, ptr [[MEMBER]], align 4
+// OGCG:   [[CLEAR:%.*]] = and i8 [[LOADVOL]], -32
+// OGCG:   [[SET:%.*]] = or i8 [[CLEAR]], 2
+// OGCG:   store volatile i8 [[SET]], ptr [[MEMBER]], align 4
+// OGCG:   ret void
+
+void clip_store_exception2(clip *c) {
+  c->a = 3;
+}
+
+// CIR:  cir.func dso_local @clip_store_exception2
+// CIR:    [[CONST:%.*]] = cir.const #cir.int<3> : !s32i
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_clip>>, !cir.ptr<!rec_clip>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][0] {name = "a"} : !cir.ptr<!rec_clip> -> !cir.ptr<!cir.array<!u8i x 3>>
+// CIR:    [[SETBF:%.*]] = cir.set_bitfield align(4) (#bfi_a1, [[MEMBER]] : !cir.ptr<!cir.array<!u8i x 3>>, [[CONST]] : !s32i) {is_volatile} -> !s32i
+// CIR:    cir.return
+
+// LLVM:define dso_local void @clip_store_exception2
+// LLVM:  [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:  [[MEMBER:%.*]] = getelementptr %struct.clip, ptr [[LOAD]], i32 0, i32 0
+// LLVM:  store volatile i24 3, ptr [[MEMBER]], align 4
+// LLVM:  ret void
+
+// OGCG: define dso_local void @clip_store_exception2
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   store volatile i24 3, ptr [[LOAD]], align 4
+// OGCG:   ret void
+
+void check_store_second_member (st4 *s4) {
+  s4->b = 1;
+}
+
+// CIR:  cir.func dso_local @check_store_second_member
+// CIR:    [[ONE:%.*]] = cir.const #cir.int<1> : !s32i
+// CIR:    [[CAST:%.*]] = cir.cast(integral, [[ONE]] : !s32i), !u64i
+// CIR:    [[LOAD:%.*]] = cir.load align(8) {{.*}} : !cir.ptr<!cir.ptr<!rec_st4>>, !cir.ptr<!rec_st4>
+// CIR:    [[MEMBER:%.*]] = cir.get_member [[LOAD]][2] {name = "b"} : !cir.ptr<!rec_st4> -> !cir.ptr<!u16i>
+// CIR:    cir.set_bitfield align(8) (#bfi_b2, [[MEMBER]] : !cir.ptr<!u16i>, [[CAST]] : !u64i) {is_volatile} -> !u64i
+
+// LLVM: define dso_local void @check_store_second_member
+// LLVM:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM:   [[MEMBER:%.*]] = getelementptr %struct.st4, ptr [[LOAD]], i32 0, i32 2
+// LLVM:   [[VAL:%.*]] = load volatile i64, ptr [[MEMBER]], align 8
+// LLVM:   [[CLEAR:%.*]] = and i64 [[VAL]], -65536
+// LLVM:   [[SET:%.*]] = or i64 [[CLEAR]], 1
+// LLVM:   store volatile i64 [[SET]], ptr [[MEMBER]], align 8
+
+// OGCG: define dso_local void @check_store_second_member
+// OGCG:   [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
+// OGCG:   [[MEMBER:%.*]] = getelementptr inbounds i64, ptr [[LOAD]], i64 1
+// OGCG:   [[LOADBF:%.*]] = load volatile i64, ptr [[MEMBER]], align 8
+// OGCG:   [[CLR:%.*]] = and i64 [[LOADBF]], -65536
+// OGCG:   [[SET:%.*]] = or i64 [[CLR]], 1
+// OGCG:   store volatile i64 [[SET]], ptr [[MEMBER]], align 8

Comment on lines +273 to +281
// LLVM: [[MEMBER:%.*]] = getelementptr %struct.st4, ptr [[LOAD]], i32 0, i32 2
// LLVM: [[VAL:%.*]] = load volatile i64, ptr [[MEMBER]], align 8
// LLVM: [[CLEAR:%.*]] = and i64 [[VAL]], -65536
// LLVM: [[SET:%.*]] = or i64 [[CLEAR]], 1
// LLVM: store volatile i64 [[SET]], ptr [[MEMBER]], align 8

// OGCG: define dso_local void @check_store_second_member
// OGCG: [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
// OGCG: [[MEMBER:%.*]] = getelementptr inbounds i64, ptr [[LOAD]], i64 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one difference from classic CodeGen in how member access is handled in the case of volatile loads. In the classic implementation, a GEP is used based on the size of the declared field, as shown here:

if (UseVolatile) {
const unsigned VolatileOffset = Info.VolatileStorageOffset.getQuantity();
if (VolatileOffset)
Addr = Builder.CreateConstInBoundsGEP(Addr, VolatileOffset);
}

Here's an example demonstrating that behavior:
https://godbolt.org/z/h98eEnd6f
In this example, the GEP is computed using the declared field type.

In CIR, however, we currently use a structure-type-based GEP instead. I believe this is semantically equivalent, but I’d like to confirm, is this correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A GEP using an integer type and a GEP using a structure type can be equivalent if they both end up computing the same pointer. In this case, you have %struct.S2 = type { i8, i32, i16 }. Assuming this is padded so that the third element occurs at offset 64 from the start of the structure, then getelementptr inbounds i64, ptr [[LOAD]], i64 1 will be equivalent to getelementptr %struct.st4, ptr [[LOAD]], i32 0, i32 2

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good.

Comment on lines +273 to +281
// LLVM: [[MEMBER:%.*]] = getelementptr %struct.st4, ptr [[LOAD]], i32 0, i32 2
// LLVM: [[VAL:%.*]] = load volatile i64, ptr [[MEMBER]], align 8
// LLVM: [[CLEAR:%.*]] = and i64 [[VAL]], -65536
// LLVM: [[SET:%.*]] = or i64 [[CLEAR]], 1
// LLVM: store volatile i64 [[SET]], ptr [[MEMBER]], align 8

// OGCG: define dso_local void @check_store_second_member
// OGCG: [[LOAD:%.*]] = load ptr, ptr {{.*}}, align 8
// OGCG: [[MEMBER:%.*]] = getelementptr inbounds i64, ptr [[LOAD]], i64 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A GEP using an integer type and a GEP using a structure type can be equivalent if they both end up computing the same pointer. In this case, you have %struct.S2 = type { i8, i32, i16 }. Assuming this is padded so that the third element occurs at offset 64 from the start of the structure, then getelementptr inbounds i64, ptr [[LOAD]], i64 1 will be equivalent to getelementptr %struct.st4, ptr [[LOAD]], i32 0, i32 2

}

/// Helper method to check if the underlying ABI is AAPCS
static bool isAAPCS(const TargetInfo &targetInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like something we should be getting through a TargetInfo function call. I'm not asking you to change that in this PR, but can you add a TODO comment? This will be the fourth place in the code (two in CIR, two in classic codegen) that performs the check this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@Andres-Salamanca Andres-Salamanca merged commit 9f7f3d6 into llvm:main Aug 4, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 4, 2025

LLVM Buildbot has detected a new failure on builder clang-hip-vega20 running on hip-vega20-0 while building clang at step 3 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/123/builds/24699

Here is the relevant piece of the build log for the reference
Step 3 (annotate) failure: '../llvm-zorg/zorg/buildbot/builders/annotated/hip-build.sh --jobs=' (failure)
...
[57/59] Linking CXX executable External/HIP/math_h-hip-6.3.0
[58/59] Building CXX object External/HIP/CMakeFiles/TheNextWeek-hip-6.3.0.dir/workload/ray-tracing/TheNextWeek/main.cc.o
[59/59] Linking CXX executable External/HIP/TheNextWeek-hip-6.3.0
+ build_step 'Testing HIP test-suite'
+ echo '@@@BUILD_STEP Testing HIP test-suite@@@'
+ ninja check-hip-simple
@@@BUILD_STEP Testing HIP test-suite@@@
[0/1] cd /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP && /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/llvm/bin/llvm-lit -sv array-hip-6.3.0.test empty-hip-6.3.0.test with-fopenmp-hip-6.3.0.test saxpy-hip-6.3.0.test memmove-hip-6.3.0.test split-kernel-args-hip-6.3.0.test builtin-logb-scalbn-hip-6.3.0.test TheNextWeek-hip-6.3.0.test algorithm-hip-6.3.0.test cmath-hip-6.3.0.test complex-hip-6.3.0.test math_h-hip-6.3.0.test new-hip-6.3.0.test blender.test
-- Testing: 14 tests, 14 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90
FAIL: test-suite :: External/HIP/blender.test (14 of 14)
******************** TEST 'test-suite :: External/HIP/blender.test' FAILED ********************

/home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/tools/timeit-target --timeout 7200 --limit-core 0 --limit-cpu 7200 --limit-file-size 209715200 --limit-rss-size 838860800 --append-exitstatus --redirect-output /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.out --redirect-input /dev/null --summary /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.time /bin/bash test_blender.sh
/bin/bash verify_blender.sh /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.out
Begin Blender test.
TEST_SUITE_HIP_ROOT=/opt/botworker/llvm/External/hip
Render /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo_release.blend
Blender 4.1.1 (hash e1743a0317bc built 2024-04-15 23:47:45)
Read blend: "/opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo_release.blend"
Could not open as Ogawa file from provided streams.
Unable to open /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.002", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.003", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.004", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.001", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
Could not open as Ogawa file from provided streams.
Unable to open /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.003", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.004", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.001", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.002", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
I0804 22:40:30.953670 3613989 device.cpp:39] HIPEW initialization succeeded
I0804 22:40:30.959429 3613989 device.cpp:45] Found HIPCC hipcc
I0804 22:40:31.029856 3613989 device.cpp:207] Device has compute preemption or is not used for display.
I0804 22:40:31.029886 3613989 device.cpp:211] Added device "" with id "HIP__0000:a3:00".
I0804 22:40:31.029964 3613989 device.cpp:568] Mapped host memory limit set to 536,444,985,344 bytes. (499.60G)
I0804 22:40:31.030225 3613989 device_impl.cpp:63] Using AVX2 CPU kernels.
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Eyepiece_rim
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.008
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.014
Fra:1 Mem:524.07M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.016
Fra:1 Mem:524.24M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.021
Fra:1 Mem:524.35M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Curve_Cables.004
Fra:1 Mem:525.52M (Peak 525.52M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.024
Fra:1 Mem:525.63M (Peak 525.63M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.025
Fra:1 Mem:525.62M (Peak 525.63M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.026
Step 12 (Testing HIP test-suite) failure: Testing HIP test-suite (failure)
@@@BUILD_STEP Testing HIP test-suite@@@
[0/1] cd /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP && /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/llvm/bin/llvm-lit -sv array-hip-6.3.0.test empty-hip-6.3.0.test with-fopenmp-hip-6.3.0.test saxpy-hip-6.3.0.test memmove-hip-6.3.0.test split-kernel-args-hip-6.3.0.test builtin-logb-scalbn-hip-6.3.0.test TheNextWeek-hip-6.3.0.test algorithm-hip-6.3.0.test cmath-hip-6.3.0.test complex-hip-6.3.0.test math_h-hip-6.3.0.test new-hip-6.3.0.test blender.test
-- Testing: 14 tests, 14 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90
FAIL: test-suite :: External/HIP/blender.test (14 of 14)
******************** TEST 'test-suite :: External/HIP/blender.test' FAILED ********************

/home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/tools/timeit-target --timeout 7200 --limit-core 0 --limit-cpu 7200 --limit-file-size 209715200 --limit-rss-size 838860800 --append-exitstatus --redirect-output /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.out --redirect-input /dev/null --summary /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.time /bin/bash test_blender.sh
/bin/bash verify_blender.sh /home/botworker/bbot/clang-hip-vega20/botworker/clang-hip-vega20/test-suite-build/External/HIP/Output/blender.test.out
Begin Blender test.
TEST_SUITE_HIP_ROOT=/opt/botworker/llvm/External/hip
Render /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo_release.blend
Blender 4.1.1 (hash e1743a0317bc built 2024-04-15 23:47:45)
Read blend: "/opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo_release.blend"
Could not open as Ogawa file from provided streams.
Unable to open /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.002", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.003", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.004", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.001", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
Could not open as Ogawa file from provided streams.
Unable to open /opt/botworker/llvm/External/hip/Blender_Scenes/290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.003", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.004", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.001", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
WARN (bke.modifier): source/blender/blenkernel/intern/modifier.cc:425 BKE_modifier_set_error: Object: "GEO-flag.002", Modifier: "MeshSequenceCache", Could not create reader for file //290skydemo2_flags.abc
I0804 22:40:30.953670 3613989 device.cpp:39] HIPEW initialization succeeded
I0804 22:40:30.959429 3613989 device.cpp:45] Found HIPCC hipcc
I0804 22:40:31.029856 3613989 device.cpp:207] Device has compute preemption or is not used for display.
I0804 22:40:31.029886 3613989 device.cpp:211] Added device "" with id "HIP__0000:a3:00".
I0804 22:40:31.029964 3613989 device.cpp:568] Mapped host memory limit set to 536,444,985,344 bytes. (499.60G)
I0804 22:40:31.030225 3613989 device_impl.cpp:63] Using AVX2 CPU kernels.
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Eyepiece_rim
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.008
Fra:1 Mem:524.00M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.014
Fra:1 Mem:524.07M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.016
Fra:1 Mem:524.24M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.021
Fra:1 Mem:524.35M (Peak 524.70M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Curve_Cables.004
Fra:1 Mem:525.52M (Peak 525.52M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.024
Fra:1 Mem:525.63M (Peak 525.63M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.025
Fra:1 Mem:525.62M (Peak 525.63M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.026
Fra:1 Mem:525.65M (Peak 525.65M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Rivets.039
Fra:1 Mem:526.00M (Peak 526.01M) | Time:00:00.75 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Hoses.003
Fra:1 Mem:537.83M (Peak 537.83M) | Time:00:00.76 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Curve_Connectors
Fra:1 Mem:537.93M (Peak 537.93M) | Time:00:00.76 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Curve_Connectors.001
Fra:1 Mem:538.36M (Peak 538.36M) | Time:00:00.76 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Eyepiece_Insides
Fra:1 Mem:539.32M (Peak 539.32M) | Time:00:00.76 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Curve_Wires
Fra:1 Mem:539.54M (Peak 539.55M) | Time:00:00.76 | Mem:0.00M, Peak:0.00M | Scene, View Layer | Synchronizing object | GEO-Eyepiece_Insides.001

bcardosolopes pushed a commit to llvm/clangir that referenced this pull request Aug 26, 2025
This PR backports support for volatile bit-fields in AAPCS.  
The corresponding upstream PRs are:  
- llvm/llvm-project#151252
- llvm/llvm-project#151875
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants